'    Copyright 2008 Jan Molnar <jan.molnar@abds.de>
'
'    This file is part of JCQ.

'    JCQ is free software: you can redistribute it and/or modify
'    it under the terms of the GNU General Public License as published by
'    the Free Software Foundation, either version 2 of the License, or
'    (at your option) any later version.

'    JCQ is distributed in the hope that it will be useful,
'    but WITHOUT ANY WARRANTY; without even the implied warranty of
'    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'    GNU General Public License for more details.

'    You should have received a copy of the GNU General Public License
'    along with JCQ. If not, see <http://www.gnu.org/licenses/>.

Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Documents

Public Class RunBinding
    Inherits DependencyObject

    Public Shared ReadOnly TextProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("Text", GetType(String), GetType(RunBinding), New FrameworkPropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf OnThisPropertyChanged)))

    Protected Shared Sub OnThisPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If e.NewValue IsNot Nothing Then
            Dim text = DirectCast(e.NewValue, String)
            Dim run = DirectCast(d, Run)

            run.Text = text
        End If
    End Sub

    Public Shared Function GetText(ByVal obj As DependencyObject) As String
        Return DirectCast(obj.GetValue(TextProperty), String)
    End Function

    Public Shared Sub SetText(ByVal obj As DependencyObject, ByVal value As String)
        obj.SetValue(TextProperty, value)
    End Sub
End Class

Public Class FlowDocumentBinding
    Inherits DependencyObject

    Public Shared ReadOnly CollectionProperty As DependencyProperty = _
    DependencyProperty.RegisterAttached("Collection", GetType(System.Collections.Specialized.INotifyCollectionChanged), GetType(FlowDocumentBinding), New FrameworkPropertyMetadata(Nothing, New PropertyChangedCallback(AddressOf OnThisPropertyChanged)))

    Private Shared lookup As New Dictionary(Of System.Collections.Specialized.INotifyCollectionChanged, DependencyObject)

    Protected Shared Sub OnThisPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
        If e.OldValue IsNot Nothing Then
            Dim collection = DirectCast(e.OldValue, System.Collections.Specialized.INotifyCollectionChanged)

            RemoveHandler collection.CollectionChanged, _
                    AddressOf OnCollectionChanged
        End If

        If e.NewValue IsNot Nothing Then
            Dim collection = DirectCast(e.NewValue, System.Collections.Specialized.INotifyCollectionChanged)

            lookup.Add(collection, d)

            Dim document As FlowDocument = DirectCast(d, FlowDocument)

            For Each item In DirectCast(collection, IEnumerable)
                AppendItemToDocument(document, item)
            Next

            AddHandler collection.CollectionChanged, _
                    AddressOf OnCollectionChanged
        End If
    End Sub

    Private Shared Sub OnCollectionChanged(ByVal sender As Object, ByVal e As System.Collections.Specialized.NotifyCollectionChangedEventArgs)
        Select Case e.Action
            Case Specialized.NotifyCollectionChangedAction.Add
                Dim item As Object = e.NewItems(0)
                Dim collection = DirectCast(sender, System.Collections.Specialized.INotifyCollectionChanged)
                Dim document As FlowDocument = DirectCast(lookup(collection), FlowDocument)

                AppendItemToDocument(document, item)
            Case Else
                Throw New NotImplementedException
        End Select
    End Sub

    Private Shared Sub AppendItemToDocument(ByVal document As FlowDocument, ByVal item As Object)
        Dim template As DataTemplate
        Dim paragraph As Paragraph

        template = DataTemplateSelector.SelectTemplate(item, document)
        paragraph = DirectCast(template.LoadContent, Paragraph)
        paragraph.DataContext = item

        document.Blocks.Add(paragraph)
    End Sub

    Public Shared Function GetCollection(ByVal obj As DependencyObject) As System.Collections.Specialized.INotifyCollectionChanged
        Return DirectCast(obj.GetValue(CollectionProperty), System.Collections.Specialized.INotifyCollectionChanged)
    End Function

    Public Shared Sub SetCollection(ByVal obj As DependencyObject, ByVal value As System.Collections.Specialized.INotifyCollectionChanged)
        obj.SetValue(CollectionProperty, value)
    End Sub
End Class

Friend Class DataTemplateSelector
    Private Class HelperPresenter
        Inherits ContentPresenter

        Public Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
            Dim m As System.Reflection.MethodInfo

            Content = item

            m = GetType(ContentPresenter).GetMethod("ChangeLogicalParent", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
            m.Invoke(Me, New Object() {container})

            Return ChooseTemplate()
        End Function
    End Class

    Public Shared Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
        Dim helper = New HelperPresenter

        Return helper.SelectTemplate(item, container)
    End Function
End Class